package com.ruyuan.commodity.common.mamager.impl;

import cn.hutool.json.JSONUtil;
import com.ruyuan.commodity.model.goods.dos.BrandDO;
import com.ruyuan.commodity.model.goods.dos.CategoryDO;
import com.ruyuan.commodity.model.goods.vo.CategoryVO;
import com.ruyuan.commodity.model.goodssearch.*;
import com.ruyuan.commodity.open.api.model.common.WebPage;
import com.ruyuan.commodity.common.mamager.BrandManager;
import com.ruyuan.commodity.common.mamager.CategoryManager;
import com.ruyuan.commodity.common.mapper.GoodsWordsMapper;
import com.ruyuan.commodity.elasticsearch.EsConfig;
import com.ruyuan.commodity.elasticsearch.EsSettings;
import com.ruyuan.commodity.exception.ServiceException;
import com.ruyuan.commodity.mq.MessageSender;
import com.ruyuan.commodity.mq.MqMessage;
import com.ruyuan.commodity.utils.*;
import com.ruyuan.commodity.common.mamager.GoodsSearchManager;
import com.ruyuan.commodity.open.api.client.goods.api.HotWordOpenApi;
import com.ruyuan.commodity.open.api.client.shop.api.ShopCatOpenApi;
import com.ruyuan.commodity.open.api.model.base.rabbitmq.AmqpExchange;
import com.ruyuan.commodity.open.api.model.pagedata.HotKeyword;
import com.ruyuan.commodity.open.api.model.shop.dos.ShopCatDO;
import org.apache.http.util.EntityUtils;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.Request;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.QueryStringQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.*;

/**
 * 基于es的商品检索
 */
@Service
public class GoodsSearchManagerImpl implements GoodsSearchManager {

    private static final Logger LOG = LoggerFactory.getLogger(GoodsSearchManagerImpl.class);

    /**
     * 商品分类业务管理器
     */
    @Autowired
    protected CategoryManager categoryManager;
    /**
     * 品牌业务管理器
     */
    @Autowired
    protected BrandManager brandManager;
    /**
     * 店铺分类的open api
     */
    @Autowired
    protected ShopCatOpenApi shopCatOpenApi;
    /**
     * es的配置类
     */
    @Autowired
    protected EsConfig esConfig;
    /**
     * MQ消息发送组件
     */
    @Autowired
    private MessageSender messageSender;
    /**
     * 商品关键词的mapper组件
     */
    @Autowired
    private GoodsWordsMapper goodsWordsMapper;
    /**
     * 热搜词的open api
     */
    @Autowired
    private HotWordOpenApi hotWordOpenApi;
    /**
     * es的Rest Client
     */
    @Autowired
    private RestHighLevelClient highLevelClient;

    public GoodsSearchManagerImpl() {
        System.setProperty("es.set.netty.runtime.available.processors", "false");
    }

    @Override
    public WebPage<GoodsSearchLine> search(GoodsSearchDTO goodsSearch) {

        int pageNo = 1;
        int pageSize = 10;

        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices(esConfig.getIndexName() + "_" + EsSettings.GOODS_INDEX_NAME) ;
        try {
            // 构建搜索条件
            SearchSourceBuilder searchSourceBuilder = createSearchCondition(goodsSearch);
            searchRequest.source(searchSourceBuilder);
            // 设置分页相关参数
            searchRequest.source().from((pageNo - 1) * pageSize).size(pageSize).explain(false);
            // 发送查询请求
            SearchResponse response = highLevelClient.search(searchRequest, RequestOptions.DEFAULT) ;
            // 解析搜索结果
            SearchHits searchHits = response.getHits();
            List<GoodsSearchLine> resultList = parseSearchResponse(searchHits);
            // 如果不为空 则将关键词异步落库
            if (!StringUtil.isEmpty(goodsSearch.getKeyword())) {
                // 发送搜索关键字的消息
                this.messageSender.send(new MqMessage(AmqpExchange.SEARCH_KEYWORDS, AmqpExchange.SEARCH_KEYWORDS + "_ROUTING", goodsSearch.getKeyword()));
            }

            return new WebPage<>(goodsSearch.getPageNo(), searchHits.getTotalHits(), goodsSearch.getPageSize(), resultList);
        } catch (Exception e) {
            LOG.error("关键词搜索时发生异常", e);
            return new WebPage<>(goodsSearch.getPageNo(), 0L, goodsSearch.getPageSize(), new ArrayList<>());
        }
    }

    /**
     * 解析搜索结果
     * @param searchHits 搜索结果
     * @return 结果集
     */
    private List<GoodsSearchLine> parseSearchResponse(SearchHits searchHits) {
        List<GoodsSearchLine> resultList = new ArrayList<>();
        for (SearchHit hit : searchHits) {
            Map<String, Object> map = hit.getSourceAsMap();
            GoodsSearchLine goodsSearchLine = new GoodsSearchLine();
            goodsSearchLine.setName(map.get("goodsName").toString());
            goodsSearchLine.setDiscountPrice(StringUtil.toDouble(map.get("discountPrice").toString(), true));
            goodsSearchLine.setThumbnail(map.get("thumbnail").toString());
            goodsSearchLine.setPrice(StringUtil.toDouble(map.get("price").toString(), 0d));
            goodsSearchLine.setGoodsId(Long.parseLong(map.get("goodsId").toString()));
            goodsSearchLine.setSmall(map.get("small").toString());
            goodsSearchLine.setCommentNum(Integer.parseInt(map.get("commentNum").toString()));
            goodsSearchLine.setBuyCount(Integer.parseInt(map.get("buyCount").toString()));
            goodsSearchLine.setGrade(StringUtil.toDouble(map.get("grade").toString(), 0d));
            goodsSearchLine.setSellerId(Long.parseLong(map.get("sellerId").toString()));
            goodsSearchLine.setSellerName(map.get("sellerName").toString());
            goodsSearchLine.setSelfOperated(map.get("selfOperated") == null ? 0 : StringUtil.toInt(map.get("selfOperated").toString(), 0));
            resultList.add(goodsSearchLine);
        }
        return resultList;
    }

    @Override
    public Map<String, Object> getSelector(GoodsSearchDTO goodsSearch) {
        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices(esConfig.getIndexName() + "_" + EsSettings.GOODS_INDEX_NAME) ;
        try {
            searchRequest.source(createSearchCondition(goodsSearch));
            // 分类
            AggregationBuilder categoryTermsBuilder = AggregationBuilders.terms("categoryAgg").field("categoryId").size(Integer.MAX_VALUE);
            // 品牌
            AggregationBuilder brandTermsBuilder = AggregationBuilders.terms("brandAgg").field("brand").size(Integer.MAX_VALUE);
            // 参数
            AggregationBuilder valuesBuilder = AggregationBuilders.terms("valueAgg").field("params.value").size(Integer.MAX_VALUE);
            AggregationBuilder paramsNameBuilder = AggregationBuilders.terms("nameAgg").field("params.name").subAggregation(valuesBuilder).size(Integer.MAX_VALUE);
            AggregationBuilder avgBuild = AggregationBuilders.nested("paramsAgg", "params").subAggregation(paramsNameBuilder);

            searchRequest.source().aggregation(categoryTermsBuilder);
            searchRequest.source().aggregation(brandTermsBuilder);
            searchRequest.source().aggregation(avgBuild);

            SearchResponse sr = highLevelClient.search(searchRequest);
            Map<String, Aggregation> aggMap = sr.getAggregations().asMap();

            Map<String, Object> map = new HashMap<>(16);

            //分类
            ParsedLongTerms categoryTerms = (ParsedLongTerms) aggMap.get("categoryAgg");
            Collection<? extends Terms.Bucket> buckets = categoryTerms.getBuckets();
            //获取所有分类
            List<CategoryVO> allCatList = this.categoryManager.listAllChildren(0L);
            //获取有商品的分类
            List<SearchSelector> catDim = SelectorUtil.createCatSelector(buckets, allCatList, goodsSearch.getCategory());
            map.put("cat", catDim);
            String catPath = null;
            if (goodsSearch.getCategory() != null) {
                CategoryDO cat = categoryManager.getModel(goodsSearch.getCategory());
                String path = cat.getCategoryPath();
                catPath = path.replace("|", Separator.SEPARATOR_PROP_VLAUE).substring(0, path.length() - 1);
            }

            List<SearchSelector> selectedCat = CatUrlUtils.getCatDimSelected(buckets, allCatList, catPath);
            //已经选择的分类
            map.put("selected_cat", selectedCat);

            //品牌
            ParsedLongTerms brandTerms = (ParsedLongTerms) aggMap.get("brandAgg");
            Collection<? extends Terms.Bucket> brandBuckets = brandTerms.getBuckets();
            List<BrandDO> brandList = brandManager.getAllBrands();
            List<SearchSelector> brandDim = SelectorUtil.createBrandSelector(brandBuckets, brandList);
            map.put("brand", brandDim);

            // 参数
            ParsedNested paramsAgg = (ParsedNested) aggMap.get("paramsAgg");
            Map<String, Aggregation> nameMap = paramsAgg.getAggregations().asMap();
            ParsedStringTerms nameTerms = (ParsedStringTerms) nameMap.get("nameAgg");

            Iterator<? extends Terms.Bucket> iterator = nameTerms.getBuckets().iterator();

            List<PropSelector> paramDim = SelectorUtil.createParamSelector(iterator);
            map.put("prop", paramDim);

            return map;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return new HashMap<>(16);
    }


    /**
     * 构建es搜索查询条件
     *
     * @return 构建好的搜索条件
     * @throws Exception 异常信息
     */
    protected SearchSourceBuilder createSearchCondition(GoodsSearchDTO goodsSearch) throws Exception {
        String keyword = goodsSearch.getKeyword();
        Long cat = goodsSearch.getCategory();
        Long brand = goodsSearch.getBrand();
        String price = goodsSearch.getPrice();
        Long sellerId = goodsSearch.getSellerId();
        Long shopCatId = goodsSearch.getShopCatId();
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        // 关键字检索
        if (!StringUtil.isEmpty(keyword)) {
            QueryStringQueryBuilder queryString = new QueryStringQueryBuilder(keyword).field("goodsName");
            queryString.defaultOperator(Operator.AND);
            // 按照关键字检索
            queryString.analyzer("ik_smart");
            queryString.tieBreaker(0.7F);
            boolQueryBuilder.must(queryString);
        }
        // 品牌搜素
        if (brand != null) {
            boolQueryBuilder.must(QueryBuilders.termQuery("brand", brand));
        }
        // 分类检索
        if (cat != null) {

            CategoryDO category = categoryManager.getModel(cat);
            if (category == null) {
                throw new ServiceException("", "该分类不存在");
            }
            boolQueryBuilder.must(QueryBuilders.wildcardQuery("categoryPath", HexUtils.encode(category.getCategoryPath()) + "*"));
        }
        //卖家搜索
        if (sellerId != null) {
            boolQueryBuilder.must(QueryBuilders.termQuery("sellerId", sellerId));
        }
        // 卖家分组搜索
        if (shopCatId != null) {
            ShopCatDO shopCat = shopCatOpenApi.getModel(shopCatId);
            if (shopCat == null) {
                throw new ServiceException("", "该分组不存在");
            }
            boolQueryBuilder.must(QueryBuilders.wildcardQuery("shopCatPath", HexUtils.encode(shopCat.getCatPath()) + "*"));
        }

        // 参数检索
        String prop = goodsSearch.getProp();
        if (!StringUtil.isEmpty(prop)) {
            String[] propArray = prop.split(Separator.SEPARATOR_PROP);
            for (String p : propArray) {
                String[] onpropAr = p.split(Separator.SEPARATOR_PROP_VLAUE);
                String name = onpropAr[0];
                String value = onpropAr[1];
                boolQueryBuilder.must(QueryBuilders.nestedQuery("params", QueryBuilders.termQuery("params.name", name), ScoreMode.None));
                boolQueryBuilder.must(QueryBuilders.nestedQuery("params", QueryBuilders.termQuery("params.value", value), ScoreMode.None));
            }
        }

        //价格搜索
        if (!StringUtil.isEmpty(price)) {
            String[] pricear = price.split(Separator.SEPARATOR_PROP_VLAUE);
            double min = StringUtil.toDouble(pricear[0], 0.0);
            double max = Integer.MAX_VALUE;

            if (pricear.length == 2) {
                max = StringUtil.toDouble(pricear[1], Double.MAX_VALUE);
            }
            boolQueryBuilder.must(QueryBuilders.rangeQuery("price").from(min).to(max).includeLower(true).includeUpper(true));
        }

        // 删除的商品不显示
        boolQueryBuilder.must(QueryBuilders.termQuery("disabled", "1"));
        // 未上架的商品不显示
        boolQueryBuilder.must(QueryBuilders.termQuery("marketEnable", "1"));
        // 待审核和审核不通过的商品不显示
        boolQueryBuilder.must(QueryBuilders.termQuery("isAuth", "1"));

        searchSourceBuilder.query(boolQueryBuilder);

        //排序
        String sortField = goodsSearch.getSort();

        String sortId = "priority";

        SortOrder sort = SortOrder.DESC;

        if (!StringUtil.isEmpty(sortField)) {

            Map<String, String> sortMap = SortContainer.getSort(sortField);

            sortId = sortMap.get("id");

            // 如果是默认排序  --默认排序根据 商品优先级排序
            if ("def".equals(sortId)) {
                sortId = "priority";
            }
            if ("buynum".equals(sortId)) {
                sortId = "buyCount";
            }
            if ("grade".equals(sortId)) {
                sortId = "commentNum";
            }

            if ("desc".equals(sortMap.get("def_sort"))) {
                sort = SortOrder.DESC;
            } else {
                sort = SortOrder.ASC;
            }
        }


        searchSourceBuilder.sort(sortId, sort);

        //好平率
        if ("commentNum".equals(sortId)) {
            searchSourceBuilder.sort("buyCount", sort);
        }

        // 如果不是默认排序 则在原有搜索结果基础上加上商品优先级排序
        if (!"priority".equals(sortId)) {
            //商品优先级
            searchSourceBuilder.sort("priority",SortOrder.DESC);
        }

        return searchSourceBuilder;
    }

    @Override
    public List<GoodsWords> getGoodsWords(String keyword) {
        return this.goodsWordsMapper.getGoodsWords("%" + keyword + "%");
    }

    @Override
    public WebPage<GoodsSearchLine> recommendGoodsList(GoodsSearchDTO goodsSearch) {
        List<HotKeyword> hotKeywords = hotWordOpenApi.listByNum(1);
        String keywords = "";
        if(StringUtil.isNotEmpty(hotKeywords)){
            keywords = hotKeywords.get(0).getHotName();
        }
        goodsSearch.setKeyword(keywords);
        return search(goodsSearch);
    }

    @Override
    public void searchGoods(GoodsSearchDTO goodsSearch) throws IOException {
        Request request = new Request("GET", "/shop/_search");

        String requestJson = "";

        request.setJsonEntity(requestJson);

        Response response = highLevelClient.getLowLevelClient().performRequest(request);
        String responseStr = EntityUtils.toString(response.getEntity());


    }
}
